/*-------------------------------------------------------------------------
 * drawElements Quality Program Tester Core
 * ----------------------------------------
 *
 * Copyright 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 *//*!
 * \file
 * \brief Internal utilities shared between TexLookup and TexCompare verifiers.
 *//*--------------------------------------------------------------------*/

#include "tcuTexVerifierUtil.hpp"
#include "tcuFloat.hpp"

namespace tcu
{
namespace TexVerifierUtil
{

float computeFloatingPointError (const float value, const int numAccurateBits)
{
	const int		numGarbageBits	= 23-numAccurateBits;
	const deUint32	mask			= (1u<<numGarbageBits)-1u;
	const int		exp				= tcu::Float32(value).exponent();

	return Float32::construct(+1, exp, (1u<<23) | mask).asFloat() - Float32::construct(+1, exp, 1u<<23).asFloat();
}

float computeFixedPointError (const int numAccurateBits)
{
	return computeFloatingPointError(1.0f, numAccurateBits);
}

Vec2 computeNonNormalizedCoordBounds (const bool normalizedCoords, const int dim, const float coord, const int coordBits, const int uvBits)
{
	const float		coordErr		= computeFloatingPointError(coord, coordBits);
	const float		minN			= coord - coordErr;
	const float		maxN			= coord + coordErr;
	const float		minA			= normalizedCoords ? minN*float(dim) : minN;
	const float		maxA			= normalizedCoords ? maxN*float(dim) : maxN;
	const float		minC			= minA - computeFixedPointError(uvBits);
	const float		maxC			= maxA + computeFixedPointError(uvBits);

	DE_ASSERT(minC <= maxC);

	return Vec2(minC, maxC);
}

void getPossibleCubeFaces (const Vec3& coord, const IVec3& bits, CubeFace* faces, int& numFaces)
{
	const float	x	= coord.x();
	const float	y	= coord.y();
	const float	z	= coord.z();
	const float ax	= de::abs(x);
	const float ay	= de::abs(y);
	const float az	= de::abs(z);
	const float ex	= computeFloatingPointError(x, bits.x());
	const float	ey	= computeFloatingPointError(y, bits.y());
	const float ez	= computeFloatingPointError(z, bits.z());

	numFaces = 0;

	if (ay+ey < ax-ex && az+ez < ax-ex)
	{
		if (x >= ex) faces[numFaces++] = CUBEFACE_POSITIVE_X;
		if (x <= ex) faces[numFaces++] = CUBEFACE_NEGATIVE_X;
	}
	else if (ax+ex < ay-ey && az+ez < ay-ey)
	{
		if (y >= ey) faces[numFaces++] = CUBEFACE_POSITIVE_Y;
		if (y <= ey) faces[numFaces++] = CUBEFACE_NEGATIVE_Y;
	}
	else if (ax+ex < az-ez && ay+ey < az-ez)
	{
		if (z >= ez) faces[numFaces++] = CUBEFACE_POSITIVE_Z;
		if (z <= ez) faces[numFaces++] = CUBEFACE_NEGATIVE_Z;
	}
	else
	{
		// One or more components are equal (or within error bounds). Allow all faces where major axis is not zero.
		if (ax > ex)
		{
			faces[numFaces++] = CUBEFACE_NEGATIVE_X;
			faces[numFaces++] = CUBEFACE_POSITIVE_X;
		}

		if (ay > ey)
		{
			faces[numFaces++] = CUBEFACE_NEGATIVE_Y;
			faces[numFaces++] = CUBEFACE_POSITIVE_Y;
		}

		if (az > ez)
		{
			faces[numFaces++] = CUBEFACE_NEGATIVE_Z;
			faces[numFaces++] = CUBEFACE_POSITIVE_Z;
		}
	}
}

Sampler getUnnormalizedCoordSampler (const Sampler& sampler)
{
	Sampler copy = sampler;
	copy.normalizedCoords = false;
	return copy;
}

static inline int imod (int a, int b)
{
	int m = a % b;
	return m < 0 ? m + b : m;
}

static inline int mirror (int a)
{
	return a >= 0 ? a : -(1 + a);
}

int wrap (Sampler::WrapMode mode, int c, int size)
{
	switch (mode)
	{
		// \note CL and GL modes are handled identically here, as verification process accounts for
		//		 accuracy differences caused by different methods (wrapping vs. denormalizing first).
		case tcu::Sampler::CLAMP_TO_BORDER:
			return deClamp32(c, -1, size);

		case tcu::Sampler::CLAMP_TO_EDGE:
			return deClamp32(c, 0, size-1);

		case tcu::Sampler::REPEAT_GL:
		case tcu::Sampler::REPEAT_CL:
			return imod(c, size);

		case tcu::Sampler::MIRRORED_ONCE:
			c = deClamp32(c, -size, size);
			// Fall-through

		case tcu::Sampler::MIRRORED_REPEAT_GL:
		case tcu::Sampler::MIRRORED_REPEAT_CL:
			return (size - 1) - mirror(imod(c, 2*size) - size);

		default:
			DE_ASSERT(DE_FALSE);
			return 0;
	}
}
} // TexVerifierUtil
} // tcu
